#include "pch.h"
#include "DXContext.h"
#include "CubeRenderer.h"
#include "Helper.h"
#include "PerformanceTimer.h"

#include <ppltasks.h>
#include <ppl.h>

using namespace concurrency;
using namespace DXBase;
using namespace DXBase::Samples;
using namespace DirectX;
using namespace Platform;
using namespace Windows::Foundation;

void CubeRenderer::Reset()
{
	m_inputLayout = nullptr;
	m_vertexBuffer = nullptr;
	m_indexBuffer = nullptr;
	m_vertexShader = nullptr;
	m_pixelShader = nullptr;
	m_constantBuffer = nullptr;
}
IAsyncOperation<bool>^ CubeRenderer::LoadAsync(DXBase::DXContext^ ctxt)
{
	auto loadVSTask = ReadDataAsync("DXBase\\CubeRendererVertexShader.cso");
	auto loadPSTask = ReadDataAsync("DXBase\\CubeRendererPixelShader.cso");

	auto createVSTask = loadVSTask.then([=](Platform::Array<byte>^ fileData) {
		HR2Ex(ctxt->m_d3dDevice->CreateVertexShader(fileData->Data, fileData->Length, nullptr, &m_vertexShader));

		const D3D11_INPUT_ELEMENT_DESC vertexDesc[] = 
		{
			{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,  D3D11_INPUT_PER_VERTEX_DATA, 0 },
			{ "COLOR",    0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		};

		HR2Ex(ctxt->m_d3dDevice->CreateInputLayout(vertexDesc, ARRAYSIZE(vertexDesc), fileData->Data, fileData->Length, &m_inputLayout));
	});

	auto createPSTask = loadPSTask.then([=](Platform::Array<byte>^ fileData) {
		HR2Ex(ctxt->m_d3dDevice->CreatePixelShader(fileData->Data, fileData->Length, nullptr, &m_pixelShader));

		CD3D11_BUFFER_DESC constantBufferDesc(sizeof(ModelViewProjectionConstantBuffer), D3D11_BIND_CONSTANT_BUFFER);
		HR2Ex(ctxt->m_d3dDevice->CreateBuffer(&constantBufferDesc, nullptr, &m_constantBuffer));
	});

	task<bool> createCubeTask = (createPSTask && createVSTask).then([=] () -> bool
	{
		VertexPositionColor cubeVertices[] = 
		{
			{XMFLOAT3(-0.5f, -0.5f, -0.5f), XMFLOAT3(0.0f, 0.0f, 0.0f)},
			{XMFLOAT3(-0.5f, -0.5f,  0.5f), XMFLOAT3(0.0f, 0.0f, 1.0f)},
			{XMFLOAT3(-0.5f,  0.5f, -0.5f), XMFLOAT3(0.0f, 1.0f, 0.0f)},
			{XMFLOAT3(-0.5f,  0.5f,  0.5f), XMFLOAT3(0.0f, 1.0f, 1.0f)},
			{XMFLOAT3( 0.5f, -0.5f, -0.5f), XMFLOAT3(1.0f, 0.0f, 0.0f)},
			{XMFLOAT3( 0.5f, -0.5f,  0.5f), XMFLOAT3(1.0f, 0.0f, 1.0f)},
			{XMFLOAT3( 0.5f,  0.5f, -0.5f), XMFLOAT3(1.0f, 1.0f, 0.0f)},
			{XMFLOAT3( 0.5f,  0.5f,  0.5f), XMFLOAT3(1.0f, 1.0f, 1.0f)},
		};

		D3D11_SUBRESOURCE_DATA vertexBufferData = {0};
		vertexBufferData.pSysMem = cubeVertices;
		vertexBufferData.SysMemPitch = 0;
		vertexBufferData.SysMemSlicePitch = 0;
		CD3D11_BUFFER_DESC vertexBufferDesc(sizeof(cubeVertices), D3D11_BIND_VERTEX_BUFFER);
		HR2Ex(ctxt->m_d3dDevice->CreateBuffer(&vertexBufferDesc, &vertexBufferData, &m_vertexBuffer));

		unsigned short cubeIndices[] = 
		{
			0,2,1, // -x
			1,2,3,

			4,5,6, // +x
			5,7,6,

			0,1,5, // -y
			0,5,4,

			2,6,7, // +y
			2,7,3,

			0,4,6, // -z
			0,6,2,

			1,3,7, // +z
			1,7,5,
		};

		m_indexCount = ARRAYSIZE(cubeIndices);

		D3D11_SUBRESOURCE_DATA indexBufferData = {0};
		indexBufferData.pSysMem = cubeIndices;
		indexBufferData.SysMemPitch = 0;
		indexBufferData.SysMemSlicePitch = 0;
		CD3D11_BUFFER_DESC indexBufferDesc(sizeof(cubeIndices), D3D11_BIND_INDEX_BUFFER);
		HR2Ex(ctxt->m_d3dDevice->CreateBuffer(&indexBufferDesc, &indexBufferData, &m_indexBuffer));

		return true;
	});

	return create_async([createCubeTask]() { return createCubeTask; });
}
void CubeRenderer::Resize(DXContext^ ctxt)
{
	float aspectRatio = 1;
	if (ctxt->Target)
	{
		auto SIZE = ctxt->Target->PixelSize;
		aspectRatio = (float)SIZE.width / (float)SIZE.height;
	}
	float fovAngleY = 70.0f * XM_PI / 180.0f;

	// Note that the m_orientationTransform3D matrix is post-multiplied here
	// in order to correctly orient the scene to match the display orientation.
	// This post-multiplication step is required for any draw calls that are
	// made to the swap chain render target. For draw calls to other targets,
	// this transform should not be applied.
	auto dorient = GetRotationMatrix(ctxt->Target->Orientation);
	XMStoreFloat4x4(
		&m_constantBufferData.projection,
		XMMatrixTranspose(
			XMMatrixMultiply(
				XMMatrixPerspectiveFovRH(
					fovAngleY,
					aspectRatio,
					0.01f,
					100.0f
					),
					XMLoadFloat4x4(&dorient)
				)
			)
		);
}
void CubeRenderer::Render(DXContext^ ctxt, PerformanceTimer^ timer)
{
	XMVECTOR eye = XMVectorSet(0.0f, 0.7f, 1.5f, 0.0f);
	XMVECTOR at = XMVectorSet(0.0f, -0.1f, 0.0f, 0.0f);
	XMVECTOR up = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);

	XMStoreFloat4x4(&m_constantBufferData.view, XMMatrixTranspose(XMMatrixLookAtRH(eye, at, up)));
	XMStoreFloat4x4(&m_constantBufferData.model, XMMatrixTranspose(XMMatrixRotationY(timer->Total * XM_PIDIV4)));

	UINT stride = sizeof(VertexPositionColor);
	UINT offset = 0;
	ctxt->m_d3dContext->IASetVertexBuffers(0, 1, m_vertexBuffer.GetAddressOf(), &stride, &offset );

	ctxt->m_d3dContext->IASetIndexBuffer(m_indexBuffer.Get(), DXGI_FORMAT_R16_UINT, 0);

	ctxt->m_d3dContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

	ctxt->m_d3dContext->IASetInputLayout(m_inputLayout.Get());

	ctxt->m_d3dContext->VSSetShader(m_vertexShader.Get(), nullptr, 0);

	ctxt->m_d3dContext->UpdateSubresource(m_constantBuffer.Get(), 0, NULL, &m_constantBufferData, 0, 0);
	ctxt->m_d3dContext->VSSetConstantBuffers(0, 1, m_constantBuffer.GetAddressOf());

	ctxt->m_d3dContext->PSSetShader( m_pixelShader.Get(), nullptr, 0);

	ctxt->m_d3dContext->DrawIndexed(m_indexCount, 0, 0);
}
